using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Jerry.UiFrame
{
    public class JList : ScrollRect, IComponent
    {
        public delegate void JItemDelegate(JListGrid item, int index);

        public event JItemDelegate OnItemRender;
        public event JItemDelegate OnItemClick;
        public event JItemDelegate OnItemDown;
        
        /// <summary>
        /// 设置选中哪个Grid，index从0开始
        /// 重新设置后，需要重新给ItemCount赋值，来刷新列表
        /// </summary>
        public int SelectedIndex
        {
            set => Selected = value;
        }
        
        private int _itemCount;
        public int ItemCount
        {
            set
            {
                _startIndex = -1;
                _itemCount = value;
                if (_grids.Count > 0)
                {
                    foreach (var item in _grids)
                    {
                        item.SetVisible(false);
                    }
                }

                if (!forceUpdateCanvas)
                    CalculateLayout();
            }
            get => _itemCount;
        }

        [SerializeField] 
        private JListGrid grid;
        [SerializeField] 
        private Vector2 spacing;

        [SerializeField][Header("强制刷新UI布局，慎用！")]
        private bool forceUpdateCanvas;
        [SerializeField] [Tooltip("多选")]
        internal bool multiple;
        [SerializeField] [Tooltip("单选时，是否允许都未选中")]
        internal bool singleOff;
        
        internal bool BDragging = false;     // 用户是否正在拖拽滚动视图
        internal bool BScrolling = false;    // 列表是否在惯性滑动
        internal int Selected = -1;    //当前选中的索引
        private List<JListGrid> _grids = new();

        private float _gridWidth;
        private float _gridHeight;
        private int _maxGrid;
        private int _maxColumns; //列
        private int _maxRows; //行
        private Vector2 _offsetPos; //Grid偏移content的位置
        private int _startIndex = -1;
        private int _endIndex;
        private bool _bInit = false;

        private bool _bScrollToAnim = false; //移动到某个index，是否播放动画
        private Vector2 _scrollToPos; // ScrollTo到的位置
        private Vector2 _offset;

        protected override void Awake()
        {
            base.Awake();

            if (forceUpdateCanvas)
            {
                StartCoroutine(GetViewport());
            }
            else
            {
                InitData();
                grid.gameObject.SetActive(false);
                onValueChanged.AddListener(OnScroll);
            }
        }

        private IEnumerator GetViewport()
        {
            yield return new WaitForEndOfFrame();
            InitData();
            grid.gameObject.SetActive(false);
            onValueChanged.AddListener(OnScroll);
            CalculateLayout();
        }
        
        
        /// <summary>
        /// 移动到指定索引位置
        /// </summary>
        /// <param name="index">从零开始</param>
        /// <param name="bHasAnim">是否播放动画</param>
        public void ScrollToIndex(int index, bool bHasAnim = false)
        {
            if (index < 0 || index >= ItemCount)
            {
                Debug.LogError($"index {index} is out of range.");
            }
            else
            {
                _bScrollToAnim = bHasAnim;
                if (horizontal)
                {
                    //总共多少列
                    var allRaw = _itemCount / _maxRows; 
                    if (_itemCount % _maxRows != 0)
                    {
                        allRaw += 1;
                    }
                    var allShowRaw = _maxGrid / _maxRows - 1; //总共显示多少列
                    
                    //计算当前index处于的列数
                    var col = index / _maxRows;
                    float ratio = 0;
                    if (col + allShowRaw - 1 >= allRaw)
                    {
                        ratio = content.sizeDelta.x - viewport.rect.width;
                    }
                    else
                    {
                        // ratio = -_offsetPos.x + (_gridWidth + spacing.x) * col + spacing.x;
                        ratio = (_gridWidth + spacing.x) * col;
                    }
                    _offset = new Vector2(-ratio, content.anchoredPosition.y);
                    if (!_bScrollToAnim)
                    {
                        content.anchoredPosition = _offset;
                        OnScroll(Vector2.zero);
                    }
                }
                else if (vertical)
                {
                    //总共多少行
                    var allRaw = _itemCount / _maxColumns;
                    if (_itemCount % _maxColumns != 0)
                    {
                        allRaw += 1;
                    }
                    var allShowRaw = _maxGrid / _maxColumns - 1; //content显示的行数

                    //计算当前index处于的行数
                    var raw = index / _maxColumns;
                    float ratio = 0;
                    if (raw + allShowRaw - 1 >= allRaw)
                    {
                        ratio = content.sizeDelta.y - viewport.rect.height;
                    }
                    else
                    {
                        // ratio = -_offsetPos.y + (_gridHeight + spacing.y) * raw - spacing.y;
                        ratio =  (_gridHeight + spacing.y) * raw;
                    }
                    
                    _offset = new Vector2(content.anchoredPosition.x, ratio);
                    if (!_bScrollToAnim)
                    {
                        content.anchoredPosition = _offset;
                        OnScroll(Vector2.zero);
                    }
                }
            }
        }

        public void SetVisible(bool bVisible)
        {
            gameObject.SetActive(bVisible);
        }

        public T GetChild<T>(string childName) where T : IComponent
        {
            var comp = transform.Find(childName).GetComponent<IComponent>();
            return (T)comp;
        }
        
        #region Unity 事件

        private void Update()
        {
            if (_bScrollToAnim)
            {
                if (vertical)
                {
                    _scrollToPos = Vector2.Lerp(content.anchoredPosition, _offset, 0.05f);
                    content.anchoredPosition = _scrollToPos;

                    if (Mathf.Abs(content.anchoredPosition.y - _offset.y) < 0.5f)
                    {
                        _bScrollToAnim = false;
                    }
                }

                if (horizontal)
                {
                    _scrollToPos = Vector2.Lerp(content.anchoredPosition, _offset, 0.05f);
                    content.anchoredPosition = _scrollToPos;
                    if (Mathf.Abs(content.anchoredPosition.x - _offset.x) < 0.5f)
                    {
                        _bScrollToAnim = false;
                    }
                }
            }
        }

        public override void OnBeginDrag(PointerEventData eventData)
        {
            base.OnBeginDrag(eventData);
            BDragging = true;
            BScrolling = true;
        }

        public override void OnEndDrag(PointerEventData eventData)
        {
            base.OnEndDrag(eventData);
            BDragging = false;
            StartCoroutine(CheckScrollingEnd());
        }

        private IEnumerator CheckScrollingEnd()
        {
            float prevPos = content.anchoredPosition.y;
            yield return new WaitForSeconds(0.1f); // 检测间隔

            // 如果位置不再变化，说明滑动停止
            while (Mathf.Abs(content.anchoredPosition.y - prevPos) > 0.01f)
            {
                prevPos = content.anchoredPosition.y;
                yield return new WaitForSeconds(0.1f);
            }

            BScrolling = false;
        }

        #endregion

        internal void ItemClick(JListGrid g)
        {
            if(multiple)
                return;
            foreach (var item in _grids)
            {
                if (item == g) continue;
                item.SetSelectedState(false);
            }
        }
        
        private void InitData()
        {
            _gridWidth = grid.RectTransform.rect.width;
            _gridHeight = grid.RectTransform.rect.height;

            _offsetPos = grid.RectTransform.anchoredPosition;

            if (vertical && horizontal)
            {
                _maxColumns = 0;
                _maxRows = 0;
                return;
            }

            if (vertical)
            {
                var totalCurRow = viewport.rect.height / (spacing.y + _gridHeight) + 2;
                int colCount = (int)(viewport.rect.width / (spacing.x + _gridWidth));
                _maxGrid = colCount * Mathf.CeilToInt(totalCurRow);
                _maxColumns = (int)(viewport.rect.width / (spacing.x + _gridWidth));
                _endIndex = _startIndex + _maxGrid - 1;
                return;
            }

            if (horizontal)
            {
                var totalCurCol = viewport.rect.width / (spacing.x + _gridWidth) + 1;
                int rawCount = (int)(viewport.rect.height / (spacing.y + _gridHeight));
                _maxGrid = rawCount * Mathf.CeilToInt(totalCurCol);
                _maxRows = (int)(viewport.rect.height / (spacing.y + _gridHeight));
                _endIndex = _startIndex + _maxGrid - 1;
            }
        }

        private void CalculateLayout()
        {
            if (vertical && horizontal)
            {
                Debug.LogError("多项滑动暂不支持");
                return;
            }

            if (!vertical && !horizontal)
            {
                Debug.LogError("vertical 和 horizontal必须选中一个");
                return;
            }

            if (vertical)
            {
                // //一行占多少高度
                var useHeight = (spacing.y + _gridHeight);
                //总共有多少行
                var totalRow = Mathf.CeilToInt(_itemCount / (float)_maxColumns);
                content.sizeDelta = new Vector2(content.sizeDelta.x, useHeight * totalRow);
                CreateGrid();
                UpdateGridVertical();
            }
            else if (horizontal)
            {
                // //一行占多少高度
                var useHeight = (spacing.x + _gridWidth);
                //总共有多少行
                var totalCol = Mathf.CeilToInt(_itemCount / (float)_maxRows);
                content.sizeDelta = new Vector2(useHeight * totalCol, content.sizeDelta.y);
                CreateGrid();
                UpdateGridHorizontal();
            }
        }

        private void OnScroll(Vector2 scroll)
        {
            if (vertical)
            {
                UpdateGridVertical();
            }
            else if (horizontal)
            {
                UpdateGridHorizontal();
            }
        }

        private void CreateGrid()
        {
            var gridCnt = _itemCount > _maxGrid ? _maxGrid : _itemCount;
            var tempCnt = _grids.Count;
            if (gridCnt > tempCnt)
            {
                for (int i = 0; i < gridCnt - tempCnt; i++)
                {
                    var go = Instantiate(grid, content.transform);
                    go.name = "Grid_" + i;
                    go.OnItemDown = OnItemDown;
                    go.OnItemClick = OnItemClick;
                    go.SetVisible(false);
                    go.Owner = this;
                    _grids.Add(go);
                }
            }
        }
        
        /// <summary>
        /// 竖直滑动时，更新列表信息
        /// </summary>
        private void UpdateGridVertical()
        {
            // 计算当前滚动位置对应的起始索引
            float contentYPos = content.anchoredPosition.y;
            int newStartIndex = Mathf.FloorToInt(contentYPos / (_gridHeight + spacing.y)) * _maxColumns;
            int newEndIndex = newStartIndex + _maxGrid - 1;

            if (newStartIndex < 0)
            {
                newStartIndex = 0;
            }

            // 如果索引未变化，则不需要更新
            if (newStartIndex == _startIndex && newEndIndex == _endIndex) return;

            // 更新显示范围内的单元格
            _startIndex = newStartIndex;
            _endIndex = newEndIndex;
            
            for (int i = _startIndex; i <= _endIndex; i++)
            {
                if (i >= _itemCount)
                    break;
            
                int index = i - _startIndex;
                var x = i % _maxColumns;
                var y = i / _maxColumns;
                _grids[index].RectTransform.anchoredPosition =
                    new Vector2(_offsetPos.x + x * (_gridWidth + spacing.x),
                        _offsetPos.y - y * (_gridHeight + spacing.y));
                OnItemRender?.Invoke(_grids[index], i);
                _grids[index].OnItemDown = OnItemDown;
                _grids[index].OnItemClick = OnItemClick;
                _grids[index].Index = i;
                _grids[index].SetSelectedState(Selected == i);
                _grids[index].SetVisible(true);
            }
        }

        /// <summary>
        /// 垂直滑动时，更新列表信息
        /// </summary>
        private void UpdateGridHorizontal()
        {
            // 计算当前滚动位置对应的起始索引
            float contentXPos = content.anchoredPosition.x;
            int newStartIndex = -Mathf.CeilToInt(contentXPos / (_gridWidth + spacing.x)) * _maxRows;
            int newEndIndex = newStartIndex + _maxGrid - 1;

            if (newStartIndex < 0)
            {
                newStartIndex = 0;
            }

            // 如果索引未变化，则不需要更新
            if (newStartIndex == _startIndex && newEndIndex == _endIndex) return;

            // 更新显示范围内的单元格
            _startIndex = newStartIndex;
            _endIndex = newEndIndex;

            for (int i = _startIndex; i <= _endIndex; i++)
            {
                if (i >= _itemCount)
                    break;

                int index = i - _startIndex;
                var x = i / _maxRows;
                var y = i % _maxRows;
                _grids[index].RectTransform.anchoredPosition =
                    new Vector2(_offsetPos.x + x * (_gridWidth + spacing.x),
                        _offsetPos.y - y * (_gridHeight + spacing.y));
                OnItemRender?.Invoke(_grids[index], i);
                _grids[index].Index = i;
                _grids[index].SetSelectedState(Selected == i);
                _grids[index].OnItemDown = OnItemDown;
                _grids[index].OnItemClick = OnItemClick;
                _grids[index].SetVisible(true);
            }
        }
    }
}